/**
 * @file EventLoop.cc
 * @author zziywang (zziywang@163.com)
 * @brief { life is too short to learn cpp! }
 * @version 1.0
 * @date 2022-05-07
 *
 * @copyright Copyright (c) 2022
 *
 */

#include "EventLoop.hh"
#include "Acceptor.hh"
#include <errno.h>
#include <iostream>
#include <stdio.h>
using std::cout;
using std::endl;

namespace snow
{

EventLoop::EventLoop(Acceptor &acceptor)
    : epfd_(epollCreate()),
      acceptor_(acceptor),
      isLooping_(false),
      eventList_(1024) // vector(1024)
{
    epollAddRead(acceptor_.getSockFd()); // 给socketfd注册读事件
}

// 事件循环
void EventLoop::loop()
{
    isLooping_ = true;
    while (isLooping_) {
        epollWait();
    }
}
// 结束循环，在另一个线程中才能结束掉
void EventLoop::unLoop()
{
    if (isLooping_) {
        isLooping_ = false;
    }
}
// epoll_wait封装
void EventLoop::epollWait()
{
    int nready;
    do {
        nready = ::epoll_wait(epfd_, &*eventList_.begin(), eventList_.size(), 10000);
    } while (-1 == nready && errno == EINTR);

    if (-1 == nready) {
        perror("epoll_wait");
        return;
    } else if (0 == nready) {
        cout << ">> epoll_wait timeout=10s!" << endl;
    } else {
        if (nready == eventList_.size()) { // 扩容
            eventList_.resize(2 * nready);
        }
        // 如有扩容，扩容完继续处理请求
        for (int i = 0; i < nready; ++i) {
            int fd = eventList_[i].data.fd;
            if (fd == acceptor_.getSockFd()) { // 处理新连接
                if (eventList_[i].events & EPOLLIN) {
                    handleNewConnection();
                }
            } else { // 处理message，又分成两种：1 是连接没断开 2 是连接断开
                if (eventList_[i].events & EPOLLIN) {
                    handleMessage(fd);
                }
            }
        }
    }
}
// epoll_wait返回两种情况：新连接到来；处理信息
void EventLoop::handleNewConnection()
{
    int peerfd = acceptor_.accept();
    epollAddRead(peerfd);
    // 构造tcp对象
    TcpConnectionPtr conn(new TcpConnection(peerfd));
    // 将回调函数交给TCP对象
    conn->setConnectionCallBack(onConnectionCb_);
    conn->setMessageCallBack(onMessageCb_);
    conn->setCloseCallBack(onCloseCb_);
    // 新连接存入map中
    tcpConns_.insert(std::make_pair(peerfd, conn));
    conn->handleNewConnectionCallBack(); // 执行NewConnectionCallBack
}
void EventLoop::handleMessage(int fd)
{
    // 根据fd从tcpMap中找
    auto it = tcpConns_.find(fd);
    if (it == tcpConns_.end()) {
        cout << "TCP CONNECTION NOT FOUND!" << endl;
        return;
    }
    if (isClientQuit(fd)) { // 判断连接是否断开，读不到数据就是断开了
        it->second->handleCloseCallBack();
        epollDelRead(fd);
        tcpConns_.erase(fd);
    } else {
        it->second->handleMessageCallBack(); // onMessage();
    }
}

bool EventLoop::isClientQuit(int fd)
{
    int ret;
    do {
        char buf[1024] = {0};
        ret = ::recv(fd, buf, sizeof(buf), MSG_PEEK);
    } while (-1 == ret && errno == EINTR);
    return 0 == ret;
}
// epoll_create，返回epfd
int EventLoop::epollCreate()
{
    int epfd = ::epoll_create1(0);
    if (-1 == epfd) {
        perror("epoll_create1");
        return -1;
    }
    return epfd;
}

void EventLoop::epollAddRead(int fd)
{
    struct epoll_event ev;
    ev.data.fd = fd;
    ev.events = EPOLLIN; // 读事件
    int ret = ::epoll_ctl(epfd_, EPOLL_CTL_ADD, fd, &ev);
    if (-1 == ret) {
        perror("epoll_ctl add read");
        return;
    }
}

void EventLoop::epollDelRead(int fd)
{
    struct epoll_event ev;
    ev.data.fd = fd;
    ev.events = EPOLLIN; // 读事件
    int ret = ::epoll_ctl(epfd_, EPOLL_CTL_DEL, fd, &ev);
    if (-1 == ret) {
        perror("epoll_ctl del read");
        return;
    }
}

void EventLoop::setConnectionCallBack(const TcpConnectionCallBack &cb)
{
    onConnectionCb_ = std::move(cb);
}
void EventLoop::setMessageCallBack(const TcpConnectionCallBack &cb)
{
    onMessageCb_ = std::move(cb);
}
void EventLoop::setCloseCallBack(const TcpConnectionCallBack &cb)
{
    onCloseCb_ = std::move(cb);
}

}; // namespace snow